package com.apimanage.gateway.filter;

import cn.hutool.core.util.StrUtil;
import com.apimanage.common.config.JwtProperties;
import com.apimanage.common.dto.UserDTO;
import com.apimanage.common.entity.Result;
import com.apimanage.common.utils.JwtTokenUtil;
import com.apimanage.gateway.config.GatewayAuthProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import java.time.Duration;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientRequestException;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 网关认证过滤器
 * 用于验证请求是否携带有效令牌，并在验证通过后传递用户信息给下游服务
 */
@Component
public class AuthFilter implements GlobalFilter, Ordered {

    private static final Logger logger = LoggerFactory.getLogger(AuthFilter.class);

    @Autowired
    private GatewayAuthProperties gatewayAuthProperties;
    

    // 使用不带负载均衡的WebClient处理内部服务调用
    @Autowired
    @Qualifier("directWebClient")
    private WebClient directWebClient;

    private final AntPathMatcher pathMatcher = new AntPathMatcher();
    
    // 定义一组无需认证的特殊路径
    private final List<String> SPECIAL_PATHS = Arrays.asList(
            "/doc.html",
            "/webjars/**",
            "/v3/api-docs",
            "/v3/api-docs/**",
            "/swagger-ui/**",
            "/swagger-ui.html"
    );
    
    /**
     * 构建并返回标准的错误响应
     */
    private Mono<Void> buildErrorResponse(ServerWebExchange exchange, HttpStatus status, String message) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(status);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        
        // 构建标准错误响应体
        String errorJson = String.format("{\"code\":\"%d\",\"message\":\"%s\",\"errorDetail\":\"%s\"}",
                status.value(), message, message);
        
        try {
            byte[] bytes = errorJson.getBytes(StandardCharsets.UTF_8.name());
            return response.writeWith(Mono.just(response.bufferFactory().wrap(bytes)));
        } catch (UnsupportedEncodingException e) {
            logger.error("Failed to encode error response", e);
            return response.setComplete();
        }
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        
        // 获取请求路径
        String requestPath = request.getURI().getPath();
        logger.debug("[AuthFilter] Processing request for path: '{}'", requestPath);
        logger.debug("[AuthFilter] Request method: '{}'", request.getMethod());
        logger.debug("[AuthFilter] Request headers: '{}'", request.getHeaders());

        // 特别处理/doc.html路径 - 直接放行（优先级最高）
        if (requestPath.equals("/doc.html")) {
            logger.info("[AuthFilter] ====== DIRECTLY ALLOWING ACCESS TO /doc.html ======");
            // 添加这条日志来确认是否成功进入该分支
            logger.debug("[AuthFilter] Successfully allowed access to /doc.html");
            return chain.filter(exchange);
        }
        
        // 特别处理/api/**/doc.html路径
        if (requestPath.contains("/api/") && requestPath.endsWith("/doc.html")) {
            logger.info("[AuthFilter] ====== DIRECTLY ALLOWING ACCESS TO API DOCS PATH: {}", requestPath);
            return chain.filter(exchange);
        }

        // 检查是否有X-Doc-Request标记
        if (request.getHeaders().containsKey("X-Doc-Request")) {
            logger.info("[AuthFilter] ====== DETECTED X-Doc-Request HEADER, ALLOWING ACCESS ======");
            return chain.filter(exchange);
        }

        // 特殊路径检查 - 直接放行
        for (String specialPath : SPECIAL_PATHS) {
            if (pathMatcher.match(specialPath, requestPath)) {
                logger.info("[AuthFilter] Path '{}' matched special path '{}', directly allowing access", requestPath, specialPath);
                return chain.filter(exchange);
            }
        }

        // 加载白名单配置
        List<String> whitelist = gatewayAuthProperties.getWhitelist();
        logger.debug("[AuthFilter] Using whitelist: {}", whitelist);

        // 检查是否匹配白名单
        boolean isWhitelistPath = false;
        String matchedPattern = null;
        
        if (whitelist != null && !whitelist.isEmpty()) {
            for (String pattern : whitelist) {
                if (StrUtil.isNotEmpty(pattern) && pathMatcher.match(pattern, requestPath)) {
                    isWhitelistPath = true;
                    matchedPattern = pattern;
                    logger.info("[AuthFilter] Path '{}' matched whitelist pattern '{}', skipping authentication", requestPath, pattern);
                    break;
                }
            }
        }

        // 如果是白名单路径，直接放行
        if (isWhitelistPath) {
            return chain.filter(exchange);
        }

        // 非白名单路径需要进行认证
        String authorizationHeader = request.getHeaders().getFirst("Authorization");
        logger.debug("[AuthFilter] Checking authorization header for path: '{}', header: {}", 
                requestPath, authorizationHeader != null ? authorizationHeader.substring(0, Math.min(10, authorizationHeader.length())) + "..." : null);

        if (StrUtil.isEmpty(authorizationHeader)) {
            logger.warn("[AuthFilter] No Authorization token found in request headers for path: '{}'", requestPath);
            return buildErrorResponse(exchange, HttpStatus.UNAUTHORIZED, "认证失败：请求头中缺少Authorization令牌");
        }

        // 提取JWT令牌（移除Bearer前缀）
        String token;
        if (authorizationHeader.startsWith("Bearer ")) {
            token = authorizationHeader.substring(7);
            logger.debug("[AuthFilter] Extracted token: {}", token.substring(0, Math.min(10, token.length())) + "...");
        } else {
            logger.warn("[AuthFilter] Invalid Authorization header format for path: '{}'", requestPath);
            return buildErrorResponse(exchange, HttpStatus.UNAUTHORIZED, "认证失败：Authorization令牌格式无效");
        }

        // 验证token并获取用户信息
        try {
            // 1. 首先尝试在网关层直接验证JWT令牌
            boolean isValidToken = JwtTokenUtil.validateToken(token);
            
            // 特别处理：如果是签名验证失败，直接返回401错误，不再尝试调用auth-center
            if (!isValidToken && logger.isDebugEnabled()) {
                // 检查令牌格式是否正确，如果格式正确但验证失败，很可能是签名问题
                String claimsCheck = JwtTokenUtil.getClaimsFromToken(token) != null ? "有效格式" : "无效格式";
                logger.debug("[AuthFilter] Token validation failed with format: {}", claimsCheck);
                // 对于无效令牌，尤其是签名错误，直接返回，避免使用无效令牌调用auth-center
                if (claimsCheck.equals("有效格式")) {
                    logger.warn("[AuthFilter] JWT signature validation failed for path: '{}', returning 401 immediately", requestPath);
                    return buildErrorResponse(exchange, HttpStatus.UNAUTHORIZED, "认证失败：无效的令牌(签名错误)");
                }
            }
            
            if (isValidToken) {
                // 网关层验证通过，直接从token中解析用户信息
                UserDTO userInfo = JwtTokenUtil.getUserInfoFromToken(token);
                
                if (userInfo != null) {
                    logger.debug("[AuthFilter] Token validated locally for user: '{}'", userInfo.getUsername());
                    
                    // 将用户信息添加到请求头，传递给下游服务
                    ServerHttpRequest modifiedRequest = request.mutate()
                            .header("X-User-Id", String.valueOf(userInfo.getId()))
                            .header("X-User-Name", userInfo.getUsername())
                            .header("X-User-Roles", userInfo.getRoles() != null ? String.join(",", userInfo.getRoles()) : "")
                            .build();
                    
                    // 使用修改后的请求继续过滤链
                    return chain.filter(exchange.mutate().request(modifiedRequest).build());
                }
            }
            
            // 2. 如果网关层验证失败但不是签名问题，则回退到调用auth-center的接口
            logger.debug("[AuthFilter] Falling back to auth-center validation for token");
            
            // 获取auth-center服务的内部地址
            String authCenterUrl = gatewayAuthProperties.getInternalService().get("auth-center");
            if (StrUtil.isEmpty(authCenterUrl)) {
                logger.error("[AuthFilter] Auth-center internal service URL not configured");
                return buildErrorResponse(exchange, HttpStatus.INTERNAL_SERVER_ERROR, "认证服务配置错误");
            }
            
            // 使用directWebClient直接调用auth-center服务，避免负载均衡器可能的错误路由
            // 修复：使用GET请求方式，移除不必要的请求体，修正URL路径
            return directWebClient.get()
                    .uri(authCenterUrl + "/getCurrentUserInfo")
                    .header("Authorization", "Bearer " + token)
                    .retrieve()
                    .bodyToMono(Result.class)
                    .timeout(Duration.ofSeconds(3))
                    .flatMap(result -> {
                        // 添加日志打印result内容
                        logger.debug("[AuthFilter] Received result from auth-center: {}", result);

                        if (result != null && result.getCode() != null && result.getCode() == 200 && result.getData() != null) {
                            // 解析用户信息
                            Map<String, Object> userData = (Map<String, Object>) result.getData();
                            UserDTO userInfo = new UserDTO();
                            if (userData.containsKey("id") && userData.get("id") != null) {
                                userInfo.setId(Long.valueOf(userData.get("id").toString()));
                            }
                            if (userData.containsKey("username") && userData.get("username") != null) {
                                userInfo.setUsername(userData.get("username").toString());
                            }
                            
                            // 处理角色信息 - UserInfoDTO有role(String)和permissions(List<String>)属性
                            List<String> roles = new ArrayList<>();
                            if (userData.containsKey("permissions") && userData.get("permissions") instanceof List) {
                                roles.addAll((List<String>) userData.get("permissions"));
                            }
                            // 如果有role属性且权限列表为空，则添加role到权限列表
                            if (userData.containsKey("role") && userData.get("role") != null && roles.isEmpty()) {
                                roles.add(userData.get("role").toString());
                            }
                            userInfo.setRoles(roles);
                            
                            // 将用户信息添加到请求头，传递给下游服务
                            ServerHttpRequest modifiedRequest = request.mutate()
                                    .header("X-User-Id", String.valueOf(userInfo.getId()))
                                    .header("X-User-Name", userInfo.getUsername())
                                    .header("X-User-Roles", userInfo.getRoles() != null ? String.join(",", userInfo.getRoles()) : "")
                                    .build();
                            
                            logger.debug("[AuthFilter] Token validated via auth-center for user: '{}'", userInfo.getUsername());
                            
                            // 使用修改后的请求继续过滤链
                            return chain.filter(exchange.mutate().request(modifiedRequest).build());
                        } else {
                            String errorMsg = "认证失败";
                            if (result != null) {
                                errorMsg += "：" + (result.getMessage() != null ? result.getMessage() : "未知错误");
                            } else {
                                errorMsg += "：无法获取用户信息";
                            }
                            logger.warn("[AuthFilter] Invalid token or failed to get user info for path: '{}', result: {}", requestPath, result);
                            return buildErrorResponse(exchange, HttpStatus.UNAUTHORIZED, errorMsg);
                        }
                    })
                    .onErrorResume(e -> {
                        logger.error("[AuthFilter] Error validating token via WebClient for path: '{}'", requestPath, e);
                        String errorMsg = "认证失败：";
                        if (e instanceof WebClientResponseException) {
                            WebClientResponseException webClientException = (WebClientResponseException) e;
                            errorMsg += "认证服务响应异常(" + webClientException.getStatusCode() + ")";
                        } else if (e instanceof WebClientRequestException) {
                            errorMsg += "认证服务请求失败";
                        } else {
                            errorMsg += "令牌验证过程出现异常";
                        }
                        return buildErrorResponse(exchange, HttpStatus.UNAUTHORIZED, errorMsg);
                    });
        } catch (Exception e) {
            logger.error("[AuthFilter] Error in token validation logic for path: '{}'", requestPath, e);
            return buildErrorResponse(exchange, HttpStatus.UNAUTHORIZED, "认证失败：系统内部错误，请联系管理员");
        }
    }

    @Override
    public int getOrder() {
        // 认证过滤器最后执行
        return -98;
    }
    

}